1 00:00:00,430 --> 00:00:01,210 Hey there. 2 00:00:01,210 --> 00:00:06,910 In this lecture, we're going to be learning about a crucial service that allows us to store data that 3 00:00:06,910 --> 00:00:10,000 we need to persist between client sessions. 4 00:00:10,000 --> 00:00:15,580 This data can be loaded and saved each time a player joins and leaves our game, so they never lose 5 00:00:15,580 --> 00:00:16,810 their progress. 6 00:00:16,810 --> 00:00:22,270 Data stores can be a scary topic for some, but the API for data stores is actually quite simple to 7 00:00:22,270 --> 00:00:24,940 use and easy to implement in your games. 8 00:00:25,060 --> 00:00:31,330 Before we get started, we must first note that testing in studio does not have access to data stores, 9 00:00:31,330 --> 00:00:35,350 so we first have to grant permission to store data in studio. 10 00:00:35,350 --> 00:00:41,380 To do so, we can go up to file, enter into our game settings and open up the security tab. 11 00:00:41,380 --> 00:00:46,450 And here we can enable studio access to API services such as data stores. 12 00:00:46,450 --> 00:00:49,690 So go ahead and enable that and press save. 13 00:00:50,320 --> 00:00:54,040 Now we can go ahead and get started learning about data stores. 14 00:00:54,040 --> 00:00:55,510 To understand data stores. 15 00:00:55,510 --> 00:00:57,520 What I'm going to do is set up a leaderboard. 16 00:00:57,520 --> 00:00:58,390 In my game. 17 00:00:58,390 --> 00:01:03,250 You probably don't know how to set up a leaderboard, so we're going to get to go through that as well. 18 00:01:03,430 --> 00:01:08,530 We can create a new script inside of our server script service, and I'm going to call this script my 19 00:01:08,530 --> 00:01:10,870 leaderboard handler. 20 00:01:12,410 --> 00:01:16,730 Inside of the script, we're going to need access to the player service. 21 00:01:16,730 --> 00:01:20,450 That way we can listen for when players leave and join our game. 22 00:01:22,400 --> 00:01:23,720 Inside of the player service. 23 00:01:23,720 --> 00:01:29,870 We're going to index for the player added event and connect a Lambda function to it and get that player 24 00:01:29,870 --> 00:01:31,670 that has joined our game. 25 00:01:31,910 --> 00:01:35,300 When a player joins our game, we're going to want to set up a leaderboard for them. 26 00:01:35,300 --> 00:01:37,640 So let's go ahead and create a function for doing that. 27 00:01:37,940 --> 00:01:44,030 I'm going to create a local function and I'm going to call this setup leaderboard for. 28 00:01:44,030 --> 00:01:47,510 And then we can pass a player instance to this function. 29 00:01:47,510 --> 00:01:50,870 What this function is going to do is it's going to create a new folder. 30 00:01:50,870 --> 00:01:52,130 So we can create a variable. 31 00:01:52,130 --> 00:01:58,430 I'm going to call it folder using the instance dot new function to create a new folder and the name 32 00:01:58,430 --> 00:01:59,450 of this folder. 33 00:01:59,450 --> 00:02:04,580 In order for it to store leaderboard data, it has to be equal to the name of leader stats. 34 00:02:04,610 --> 00:02:06,980 No capital letters, no spaces. 35 00:02:06,980 --> 00:02:09,380 It must be named exactly like this. 36 00:02:09,650 --> 00:02:11,960 Afterwards, we can set the parent. 37 00:02:12,600 --> 00:02:15,630 Of this folder equal to our player. 38 00:02:17,270 --> 00:02:23,960 Now to actually store information in this, what we can do is we can create some, uh, value instances 39 00:02:23,960 --> 00:02:28,730 like number values or string values, and those will display inside of our leaderboard. 40 00:02:28,730 --> 00:02:34,460 So for this example I want to create, let's say a new number value that's going to represent the player's 41 00:02:34,460 --> 00:02:35,240 money. 42 00:02:35,240 --> 00:02:37,850 So we can create a variable I'll call it money. 43 00:02:38,180 --> 00:02:41,600 And we'll create a new number value. 44 00:02:42,830 --> 00:02:48,260 And we're going to set the name of this number, value equal to whatever we would like to display on 45 00:02:48,260 --> 00:02:48,800 our leaderboard. 46 00:02:48,800 --> 00:02:50,570 In this case we'll just call it money. 47 00:02:51,470 --> 00:02:57,800 And then we can go ahead and set the parent of money equal to our players Leaderstats folder. 48 00:02:57,800 --> 00:03:00,950 So we can actually just reference our folder variable here. 49 00:03:01,160 --> 00:03:06,830 And that's all we need to do to set up a money number value for our players leaderboard. 50 00:03:06,830 --> 00:03:11,660 Then we can call this function within our lambda function and pass the player. 51 00:03:11,960 --> 00:03:18,170 And now every time a player joins our game, we're going to set up, uh, an individual, uh, money 52 00:03:18,170 --> 00:03:20,150 number value for each player. 53 00:03:20,570 --> 00:03:25,880 Now, since this event may not fire while we're testing in studio because our player joins the game 54 00:03:25,880 --> 00:03:33,380 first, then what we could do is we could loop through every single player inside of players, get players. 55 00:03:34,330 --> 00:03:37,990 And then we can set up the leaderboard for each one of those players. 56 00:03:39,000 --> 00:03:41,700 So now we have a basic leaderboard system set up. 57 00:03:41,700 --> 00:03:43,770 And if we go and playtest our game. 58 00:03:46,680 --> 00:03:52,860 As you can see now we have a money category in the leaderboard and currently my value is set to zero. 59 00:03:52,890 --> 00:03:58,590 However, if we go to the server and if the server changes the value, as you can see, here's our Leaderstats 60 00:03:58,590 --> 00:03:59,190 folder. 61 00:03:59,190 --> 00:04:01,650 And then here's our money number value. 62 00:04:01,650 --> 00:04:07,470 If the server updates this value to, let's say we now have $100 and we go back to the client. 63 00:04:07,470 --> 00:04:10,920 As you can see it updates within our leaderboard as well. 64 00:04:11,510 --> 00:04:15,890 So when you have a leaderboard in your game, there's many cases where you would actually like to save 65 00:04:15,890 --> 00:04:20,540 this data and reload the data next time this player joins your game. 66 00:04:20,540 --> 00:04:22,760 And this is what we use data stores for. 67 00:04:23,330 --> 00:04:30,140 So in order to implement a data store into our server script, we're going to need to get the data store 68 00:04:30,140 --> 00:04:30,830 service. 69 00:04:30,830 --> 00:04:32,870 So we'll create a new variable. 70 00:04:32,870 --> 00:04:35,360 We'll call it data store service. 71 00:04:35,360 --> 00:04:40,130 And it's going to be equal to Game Get service data Store service. 72 00:04:40,580 --> 00:04:45,860 Now inside of this data store service we have functions to create or get a data store. 73 00:04:45,860 --> 00:04:48,200 So what I'm going to do is I'm going to create a variable. 74 00:04:48,200 --> 00:04:51,950 And this variable is going to represent my money data store. 75 00:04:51,950 --> 00:04:54,230 So I can call it money data store. 76 00:04:54,910 --> 00:05:00,430 And inside of the data store service, there is a function we could call, which is called Get Data 77 00:05:00,430 --> 00:05:05,860 Store and it says creates a data store instance with the provided name and scope. 78 00:05:05,860 --> 00:05:10,270 Now, if this data store with this name does not exist, then it will create it. 79 00:05:10,270 --> 00:05:15,400 However, if this data store already exists in your game with this name, then instead it will return 80 00:05:15,400 --> 00:05:17,740 that data store instead of creating a new one. 81 00:05:17,740 --> 00:05:24,100 So we can go ahead and name this data store for example player money. 82 00:05:24,710 --> 00:05:31,250 So now we have a money data store that we can load and save data inside of any time a player leaves 83 00:05:31,250 --> 00:05:32,420 or joins our game. 84 00:05:33,250 --> 00:05:36,970 So what I'm going to do now is I'm going to create two functions. 85 00:05:37,210 --> 00:05:41,470 I'm going to call this function get money data for. 86 00:05:41,470 --> 00:05:44,440 And we can pass a player instance to this function. 87 00:05:44,440 --> 00:05:46,330 And I'm going to create another function. 88 00:05:46,330 --> 00:05:50,020 And I'm going to call this one save money data for. 89 00:05:50,020 --> 00:05:52,720 And we'll also pass a player instance to this function. 90 00:05:53,780 --> 00:06:01,490 Now, in order to save data to our data store, we first need to set a key that's going to be associated 91 00:06:01,490 --> 00:06:02,300 with a value. 92 00:06:02,300 --> 00:06:08,810 Because data stores are basically like dictionaries, data stores have a key, and that key is associated 93 00:06:08,810 --> 00:06:09,800 with a value. 94 00:06:09,830 --> 00:06:16,100 Now, since we want the data store to store unique data for every single player that plays our game, 95 00:06:16,100 --> 00:06:20,570 then we need to figure out how to create a special key for each player. 96 00:06:20,570 --> 00:06:25,130 So that way when they leave or come back into the game, we always get their same data. 97 00:06:25,220 --> 00:06:30,740 Now, you might think of using the players name as the key for the data store, but this is actually 98 00:06:30,740 --> 00:06:36,170 a bad practice, and that's because players are able to change their name, which means if a player 99 00:06:36,170 --> 00:06:41,540 plays your game, saves data, but then later they change their name and come back to your game, they 100 00:06:41,540 --> 00:06:43,460 won't have access to their data anymore. 101 00:06:43,460 --> 00:06:49,910 So the best thing to use for saving player data is going to be the player's user ID, which is unique 102 00:06:49,910 --> 00:06:51,800 for every single player in your game. 103 00:06:52,880 --> 00:06:58,400 So when we want to save some money for our player, we can pass the player, and then we can also pass 104 00:06:58,400 --> 00:07:01,760 the amount of money that we would like to save for this player. 105 00:07:02,840 --> 00:07:04,070 Now to save data. 106 00:07:04,070 --> 00:07:10,850 There's a function in our money data store called set a sync and it says sets the value of the data 107 00:07:10,850 --> 00:07:12,110 store for the given key. 108 00:07:12,140 --> 00:07:15,440 So you pass a key and you pass a value. 109 00:07:15,440 --> 00:07:19,160 And that key will be given that value inside of your data store. 110 00:07:19,940 --> 00:07:23,540 Now this is an easy and quick way to set data inside of your data store. 111 00:07:23,570 --> 00:07:27,950 If you do not care about the previous value that was stored at this key. 112 00:07:28,010 --> 00:07:34,820 However, in times when you want to prevent data loss in your data stores, it may be more smart to 113 00:07:34,820 --> 00:07:37,760 use the update async function instead. 114 00:07:37,790 --> 00:07:44,840 And what this does is it allows you to see the previous value stored at a particular key, and from 115 00:07:44,840 --> 00:07:48,770 that point, you can make decisions on whether or not you want to save the data or not. 116 00:07:48,800 --> 00:07:54,320 For example, maybe the player joined your game and you failed to load data for them and you want to 117 00:07:54,320 --> 00:07:55,580 go update their data. 118 00:07:55,580 --> 00:08:02,330 If you notice that their current data in game is off to what is saved in your data store, then you 119 00:08:02,330 --> 00:08:07,160 can be like, ah, I don't want to update data for this player and avoid data loss. 120 00:08:07,770 --> 00:08:14,490 So in most cases I'd recommend using update async, but you do want to be mindful of the limitations 121 00:08:14,490 --> 00:08:18,750 of data stores outlined in the Roblox API documentation. 122 00:08:19,110 --> 00:08:23,730 Inside of the Roblox documentation, there's a section talking about limits for data stores. 123 00:08:23,730 --> 00:08:27,420 It states that there are also limits applied to the data store model. 124 00:08:27,420 --> 00:08:32,010 If an experience exceeds these limits, the service automatically throttles the experiences. 125 00:08:32,010 --> 00:08:36,990 Data store usage, causing requests to be placed in a queue. 126 00:08:36,990 --> 00:08:41,970 When a limit is reached, further requests are placed into one of four queues set, ordered, set, 127 00:08:41,970 --> 00:08:46,620 get and ordered Get requests in a queue are handled in the order they are received, and the called 128 00:08:46,620 --> 00:08:50,580 function continues to yield as long as its request is queued. 129 00:08:50,580 --> 00:08:55,590 If the data store key itself is throttled, the request is temporarily skipped, but still in the queue. 130 00:08:55,620 --> 00:09:01,020 Each queue has a limit of 30 requests, and when this limit is exceeded, the request will fail with 131 00:09:01,020 --> 00:09:07,050 an error code and the 301 to 306 range indicating that the request was dropped entirely. 132 00:09:07,230 --> 00:09:13,860 So here Roblox shows us the different limits with the different functions inside of our data store. 133 00:09:14,070 --> 00:09:21,720 So as you can see, for example, our set async would count against the set limit in our data store. 134 00:09:21,720 --> 00:09:26,910 There's another function here called get async, which allows us to grab the value stored at a particular 135 00:09:26,910 --> 00:09:27,360 key. 136 00:09:27,360 --> 00:09:31,020 And this function counts against the get limit. 137 00:09:31,230 --> 00:09:36,150 So here is the limit that we are given for each one of our servers. 138 00:09:36,150 --> 00:09:39,570 It's equal to the number of players multiplied by ten. 139 00:09:39,570 --> 00:09:40,860 And this is per minute. 140 00:09:40,860 --> 00:09:47,940 So each player is allowed ten requests per minute plus an additional 60 in case you need them. 141 00:09:47,940 --> 00:09:54,090 So every single minute that passes by in your game, each player in your game is allowed ten requests 142 00:09:54,090 --> 00:10:00,540 to a data store for the get set, and the other values down here that we're not going to worry about 143 00:10:00,540 --> 00:10:01,680 too much. 144 00:10:02,070 --> 00:10:07,290 These ones are given less requests, but the ones that you're going to be using the most are these ones 145 00:10:07,290 --> 00:10:07,890 up here. 146 00:10:07,890 --> 00:10:13,410 And you get ten requests per each play in your game, plus an additional 60. 147 00:10:14,130 --> 00:10:18,450 Another area we want to be mindful of with our data stores are throughput limits. 148 00:10:18,450 --> 00:10:24,360 So as you can see here, every single minute for each of our servers, we have a 25 megabyte limit for 149 00:10:24,360 --> 00:10:27,960 reading and a four megabyte writing to data stores. 150 00:10:28,110 --> 00:10:33,720 So they get async function will count against your read limit, while for example, the set or remove 151 00:10:33,720 --> 00:10:36,270 async functions will count against your write limit. 152 00:10:36,270 --> 00:10:42,270 However, the update async function, since it takes into account both the previous value and overwriting 153 00:10:42,270 --> 00:10:43,350 it with a new value. 154 00:10:43,350 --> 00:10:46,800 That function will count against both your read and your write limit. 155 00:10:46,800 --> 00:10:50,880 So you just want to be mindful about the amount of data that you're saving to your data stores, and 156 00:10:50,880 --> 00:10:57,480 make sure you do not exceed the 25 megabyte per minute read or the four megabyte per minute write throughput 157 00:10:57,480 --> 00:10:58,200 limits. 158 00:10:58,200 --> 00:11:02,910 You won't likely ever reach these limits unless you're storing a lot of data for some reason, so I 159 00:11:02,910 --> 00:11:04,260 wouldn't worry too much about this. 160 00:11:04,260 --> 00:11:08,790 But again, you always want to be mindful with how frequently you are calling the different functions 161 00:11:08,790 --> 00:11:10,110 for your data stores. 162 00:11:10,910 --> 00:11:17,120 So to save data for this player, we can use set async and the API for this is very simple. 163 00:11:17,120 --> 00:11:19,070 You would just pass the key. 164 00:11:19,070 --> 00:11:24,530 In this case we will be using the player's user id and then the value that we would like to store at 165 00:11:24,530 --> 00:11:27,770 this key would be the amount passed to this function. 166 00:11:27,770 --> 00:11:30,320 And that's how you would use the set async function. 167 00:11:30,320 --> 00:11:33,920 Now to use the update async function it's a little bit different. 168 00:11:33,920 --> 00:11:38,180 You pass the key, but this time you need to pass a function. 169 00:11:38,540 --> 00:11:44,510 And what this function does is that it gets passed the previous data that was stored at this particular 170 00:11:44,510 --> 00:11:49,340 key, and we'll just call it old data here in the parameter section for our function. 171 00:11:49,430 --> 00:11:54,140 From this point, you can check anything you would like with this old data. 172 00:11:54,140 --> 00:11:56,450 You can make sure that everything looks good. 173 00:11:56,450 --> 00:12:01,910 The player didn't error previously or whatever, and if everything is good to go, then you can update 174 00:12:01,910 --> 00:12:04,820 the previous data with whatever value you would like. 175 00:12:04,820 --> 00:12:10,070 In this case, I would like to update all data with this new amount, and then you would return this 176 00:12:10,070 --> 00:12:16,940 new data back to when this lambda function gets called internally by the update async function. 177 00:12:16,940 --> 00:12:24,770 So this is how you would use update async to get and then set player data for a particular key. 178 00:12:25,280 --> 00:12:31,580 Now something very important to note about these async functions is that they are a synchronous and 179 00:12:31,580 --> 00:12:34,580 asynchronous functions are prone to errors. 180 00:12:34,580 --> 00:12:41,240 And this is because since these interact with Roblox cloud, there could be instances where an error 181 00:12:41,240 --> 00:12:44,930 occurs online somewhere that's completely out of your control. 182 00:12:44,930 --> 00:12:50,090 So you want to prevent your scripts from halting execution and encountering an error. 183 00:12:50,090 --> 00:12:54,080 And this is where we would use the pcall or protected call function. 184 00:12:54,080 --> 00:12:54,530 Right. 185 00:12:54,890 --> 00:12:57,230 So we can call Pcall. 186 00:12:57,230 --> 00:12:59,720 And then we can pass the lambda function in here. 187 00:12:59,720 --> 00:13:05,000 And what we want to do is we want to copy this here and then just paste that inside of our pcall. 188 00:13:05,000 --> 00:13:11,450 So that way if this does error, our pcall is going to catch it and it's not going to halt the execution 189 00:13:11,450 --> 00:13:12,500 in our script. 190 00:13:12,650 --> 00:13:19,100 And of course, we can also store the success and then the error from this pcall. 191 00:13:19,100 --> 00:13:22,100 And we can check whether or not if we were successful. 192 00:13:22,100 --> 00:13:28,400 So if we were not successful in storing this player's data, then we can go ahead and warn in the console 193 00:13:28,400 --> 00:13:35,540 that we failed to save player data for a particular player, and we could pass some extra information 194 00:13:35,540 --> 00:13:40,100 like the player's user ID as well as the error that occurred. 195 00:13:40,100 --> 00:13:45,560 And that's how you would save data for a player using the update async function. 196 00:13:46,400 --> 00:13:49,400 Now, how about loading or getting data for a player? 197 00:13:49,430 --> 00:13:51,320 Well, that's very easy as well. 198 00:13:51,990 --> 00:14:00,120 What we would do is we would use inside of our money data store the git async function, and we would 199 00:14:00,120 --> 00:14:04,920 simply pass the key, which would be our player's user ID, and that's it. 200 00:14:04,920 --> 00:14:09,900 This will return back to us whatever value is stored at this key. 201 00:14:09,930 --> 00:14:16,740 However, this function is also prone to errors, so that means you need to wrap it in a p call. 202 00:14:16,740 --> 00:14:18,930 So we'll do this exact same thing here. 203 00:14:19,700 --> 00:14:21,080 And we'll copy this. 204 00:14:21,080 --> 00:14:27,650 But instead what we want to do is we want to return the value from this get async function call. 205 00:14:27,650 --> 00:14:31,520 So we'll just put a return statement here and there we go. 206 00:14:31,610 --> 00:14:35,180 Now we can store whether or not this call was successful. 207 00:14:35,180 --> 00:14:37,430 And then the result from the p call. 208 00:14:37,430 --> 00:14:41,420 If the p call errors then this result is going to store our error message. 209 00:14:41,420 --> 00:14:46,820 However, if we're successful then this result variable is going to store whatever value was stored 210 00:14:46,820 --> 00:14:48,500 inside of our data store. 211 00:14:48,830 --> 00:14:54,530 So then we can go ahead and check if we were successful and if we were, we can go ahead and return 212 00:14:54,530 --> 00:14:55,370 the result. 213 00:14:56,210 --> 00:15:03,170 Otherwise, if we were not successful, we could say something like failed to grab money data for player. 214 00:15:03,170 --> 00:15:09,170 And again we could pass the player's user ID and then whatever the error message was. 215 00:15:09,750 --> 00:15:14,160 And since we don't want to return nothing from this function, we'll just go ahead and return the value 216 00:15:14,160 --> 00:15:14,940 of zero. 217 00:15:15,730 --> 00:15:19,570 So now using these two functions we can go ahead and update our code. 218 00:15:19,660 --> 00:15:24,910 For example, here, when we set up a leaderboard for a player, we want to update the value inside 219 00:15:24,910 --> 00:15:28,930 of this money number value to whatever is stored in the data store. 220 00:15:29,080 --> 00:15:33,250 So when we create this new money instance, set the name equal to money. 221 00:15:33,610 --> 00:15:40,690 Then we can set the value inside of here equal to what gets returned from our get money data for function. 222 00:15:40,690 --> 00:15:42,190 And we'll pass our player here. 223 00:15:42,960 --> 00:15:48,660 So now this function will either return whatever value is stored in the data store, or the value of 224 00:15:48,660 --> 00:15:49,980 zero by default. 225 00:15:50,430 --> 00:15:57,540 And actually, one more thing we need to check for is that this result that is returned from our Get 226 00:15:57,540 --> 00:16:03,870 async function actually might be nil, because if a new player joins your game, obviously they're not 227 00:16:03,870 --> 00:16:06,480 going to have any data stored in this data store. 228 00:16:06,480 --> 00:16:10,950 So when they get async function is called it might return nil. 229 00:16:10,950 --> 00:16:13,950 But the p call will say that it was successful. 230 00:16:13,950 --> 00:16:19,620 So what we need to check for is that we also got a result from our p call. 231 00:16:19,620 --> 00:16:26,700 So what we could do is if we were successful, then what we're going to do is we're going to check if 232 00:16:26,700 --> 00:16:27,720 we have a result. 233 00:16:27,720 --> 00:16:32,850 So if we have or if we don't have a result, if we don't have a result, then we need to set result 234 00:16:32,850 --> 00:16:35,490 equal to a default value of like zero. 235 00:16:35,490 --> 00:16:38,310 And then we can go ahead and return the result. 236 00:16:38,310 --> 00:16:43,530 So this will catch when we don't have any data stored for new players that join our game. 237 00:16:44,550 --> 00:16:49,710 Now the next thing we can go ahead and listen to is when a player is removed from our game. 238 00:16:49,710 --> 00:16:54,600 So inside of the player service, we can access the player removing event and connect a function to 239 00:16:54,600 --> 00:16:57,360 this and get the player that is leaving our game. 240 00:16:57,360 --> 00:17:02,190 And then we can go ahead and call our save money data for event. 241 00:17:02,190 --> 00:17:07,320 We can pass our player, and then we can go ahead and get the amount that we would like to save. 242 00:17:07,320 --> 00:17:10,710 And again, that's going to be stored inside of the player's leaderstats folder. 243 00:17:10,710 --> 00:17:17,400 So we can index the player for Leaderstats and then get the money number value. 244 00:17:17,400 --> 00:17:21,840 And then we can go ahead and pass the value that is stored in that instance. 245 00:17:22,430 --> 00:17:28,910 So now any time a player joins our game or leaves our game, we're going to be loading or saving data 246 00:17:28,910 --> 00:17:29,600 for them. 247 00:17:29,630 --> 00:17:31,790 So now if we go and play test our game. 248 00:17:34,840 --> 00:17:38,920 Here we get the value of zero for our money. 249 00:17:38,920 --> 00:17:45,640 If I go to the server and I decide to update our money to, let's say, $100. 250 00:17:46,270 --> 00:17:53,530 Now we have $100 for my player, and then if my player decides to leave the game, then that's going 251 00:17:53,530 --> 00:17:55,690 to be stored inside of our data store. 252 00:17:55,690 --> 00:18:01,840 And that means if I play my game again and join a new session, we should get that money back inside 253 00:18:01,840 --> 00:18:02,740 of our data store. 254 00:18:02,740 --> 00:18:09,070 And just like that, as you can see, our money of $100 is back with us in our game. 255 00:18:09,070 --> 00:18:11,710 And that's how you use data stores. 256 00:18:11,710 --> 00:18:16,690 You just store data in the cloud, and then you take that data back whenever you need it. 257 00:18:16,690 --> 00:18:21,910 Of course, there are limits that you need to be aware of, but most of the times you won't exceed those 258 00:18:21,910 --> 00:18:24,700 limits unless you're doing something wrong in your code. 259 00:18:25,270 --> 00:18:30,700 Now, there are a couple of other things we need to account for when saving player data. 260 00:18:30,790 --> 00:18:38,500 For example, let's say all the servers in your game get shut down because you've just passed a new 261 00:18:38,500 --> 00:18:43,300 update and you wanted to close all the servers and start all fresh ones for the new update. 262 00:18:43,480 --> 00:18:50,680 Well, there might be a case where we won't be able to save data for all of the players in your game, 263 00:18:50,680 --> 00:18:51,910 and that's a problem. 264 00:18:51,910 --> 00:18:59,530 So to fix that, if we were to refer to game, there's a function in there called bind to close and 265 00:18:59,530 --> 00:19:03,400 it says binds a function to be called before the game shuts down. 266 00:19:03,400 --> 00:19:09,100 So before any of your servers shut down, this function that is passed in here is going to execute. 267 00:19:09,100 --> 00:19:14,980 And this is where we could go ahead and save all of the data for the players in our server before the 268 00:19:14,980 --> 00:19:16,120 game closes. 269 00:19:16,590 --> 00:19:23,370 We may also want to save player data periodically, like every five minutes, to account for possible 270 00:19:23,370 --> 00:19:29,370 crashes, because if a server crashes, this bind to close function will not execute. 271 00:19:29,370 --> 00:19:36,510 So another thing that developers do is that they create a while true do loop, and they have a forever 272 00:19:36,510 --> 00:19:40,590 loop running that runs, let's say every five minutes. 273 00:19:40,590 --> 00:19:42,600 So we would wait 300 seconds. 274 00:19:42,600 --> 00:19:47,670 So every five minutes it goes and saves all the data for the players in our game. 275 00:19:47,670 --> 00:19:53,400 Now, since we want both of these, um, blocks of code to do the same thing, we can go ahead and create 276 00:19:53,400 --> 00:19:59,430 a function specifically for saving all of the money data for all of the players in our game, so we 277 00:19:59,430 --> 00:20:01,230 can create a new function. 278 00:20:01,320 --> 00:20:03,780 I'm going to call this function. 279 00:20:05,230 --> 00:20:09,310 Uh, we'll call it save money data for all players. 280 00:20:09,700 --> 00:20:13,750 And all this function is going to do is it's going to loop through every single player in our game. 281 00:20:13,750 --> 00:20:17,110 So we're going to do players get players. 282 00:20:18,720 --> 00:20:23,010 And what we want to do is we want to save their money data. 283 00:20:23,010 --> 00:20:27,150 So we would call the save money data for function and pass this player. 284 00:20:27,660 --> 00:20:34,920 Now the problem is, is that the update async function and the get async function they yield. 285 00:20:34,920 --> 00:20:41,250 So when the threat of execution hits this block of code, it's going to be sitting here waiting right 286 00:20:41,250 --> 00:20:43,230 here and also right here. 287 00:20:43,320 --> 00:20:48,960 And if the game is shutting down or we want to save all the data for all the players in our game, we 288 00:20:48,960 --> 00:20:53,580 don't want to have to sit here and yield for every single player as we save their data. 289 00:20:53,580 --> 00:20:59,670 So the best thing that we could do is we could use the task library and use the spawn function to spawn 290 00:20:59,670 --> 00:21:01,350 this function in a new thread. 291 00:21:01,350 --> 00:21:05,850 That way we aren't halting the thread of execution in this for loop. 292 00:21:07,090 --> 00:21:12,730 So now we have a function that goes through all of the players in our game and saves all of the money 293 00:21:12,730 --> 00:21:14,260 data for these players. 294 00:21:14,410 --> 00:21:17,500 And we also need to make sure that we pass the amount as well. 295 00:21:17,500 --> 00:21:23,110 So again we'll refer to our player, get their leader stats folder, get the money number value that 296 00:21:23,110 --> 00:21:26,080 is in there, and then get the value stored in that instance. 297 00:21:26,740 --> 00:21:30,310 And now we can go down and call this function here. 298 00:21:31,200 --> 00:21:34,230 And then we can also go ahead and call this function here. 299 00:21:35,070 --> 00:21:41,190 Now we have a complete data source system set up where it goes, and it saves data for all the players 300 00:21:41,190 --> 00:21:43,410 in our game every five minutes. 301 00:21:43,410 --> 00:21:45,750 It saves all the data in our game. 302 00:21:45,750 --> 00:21:48,030 When the game closes down. 303 00:21:48,030 --> 00:21:52,140 We also get all of the data for players when they join the game. 304 00:21:52,140 --> 00:21:55,800 And then we also save data for players when they leave the game. 305 00:21:56,160 --> 00:22:05,310 One more thing that we need to account for is let's say we grab money data for a player, but it errored 306 00:22:05,310 --> 00:22:07,410 and we failed to grab their data. 307 00:22:07,440 --> 00:22:15,300 Well, if a player, let's say, had $1 million in your data store, but then an error occurred and 308 00:22:15,300 --> 00:22:19,560 we weren't able to grab their data and instead we just returned zero. 309 00:22:19,650 --> 00:22:25,710 That means now we're storing the value of zero inside of that number value money instance. 310 00:22:25,710 --> 00:22:31,440 And then when that player leaves the game, we're overwriting their data with this new value of zero, 311 00:22:31,440 --> 00:22:33,630 which could result in data loss. 312 00:22:33,630 --> 00:22:38,520 So to prevent that issue, what we can do is we can create a table. 313 00:22:38,520 --> 00:22:42,150 And I'm going to call this table Errored players. 314 00:22:43,360 --> 00:22:51,250 And any time we use the get money data for function and we fail to grab the data for a particular player, 315 00:22:51,250 --> 00:22:55,120 then we can store that player inside of our Arab players table. 316 00:22:55,120 --> 00:23:00,700 So that way, any time we call this function right here, we can check if that particular player errored. 317 00:23:00,700 --> 00:23:03,820 And if they did, we're not going to save their data. 318 00:23:04,150 --> 00:23:09,430 So right down here, if we have failed to grab money data for this player, then inside of our Arab 319 00:23:09,430 --> 00:23:13,150 players table, we'll create a new key using the players name. 320 00:23:13,900 --> 00:23:17,080 And we're going to set this equal to true to signify. 321 00:23:17,080 --> 00:23:19,480 Yep, they aired that way. 322 00:23:19,480 --> 00:23:25,930 Any time we call this function, we could go ahead and check if inside of the aired players table this 323 00:23:25,930 --> 00:23:27,520 player exists. 324 00:23:27,850 --> 00:23:31,960 And if they did, then we can go ahead and just exit out of this function. 325 00:23:31,960 --> 00:23:35,530 And we can also give a warning like cannot. 326 00:23:36,800 --> 00:23:40,190 Save data for Errored players. 327 00:23:40,870 --> 00:23:47,740 One last thing we need to mention is that when you're play testing in studio and you set up, for example, 328 00:23:47,740 --> 00:23:53,470 a multiple player server, the players that are going to be present in these test servers are not actual 329 00:23:53,470 --> 00:23:54,400 real players. 330 00:23:54,400 --> 00:24:01,120 They're user IDs are going to be of a negative value, and you do not want to store negative values 331 00:24:01,120 --> 00:24:06,040 as keys inside of your data stores, because that's going to cause problems. 332 00:24:06,040 --> 00:24:14,140 So any time when you enable, um, access to API services inside of your game, you need to be very 333 00:24:14,140 --> 00:24:17,950 careful to not store any user IDs that are negative. 334 00:24:18,040 --> 00:24:24,370 And we can easily prevent that from happening by going to our save money data for function and checking 335 00:24:24,370 --> 00:24:26,800 if this player's user ID is negative. 336 00:24:27,070 --> 00:24:27,910 So. 337 00:24:28,790 --> 00:24:36,560 If this player dot user id is less than, let's say one, then we can go ahead and warn and say something 338 00:24:36,560 --> 00:24:42,320 like unable to save data for non real player accounts. 339 00:24:43,120 --> 00:24:45,400 And then we'll just return out of this function. 340 00:24:45,400 --> 00:24:50,740 We can also basically do the exact same thing in our Get Money data for function two. 341 00:24:50,740 --> 00:24:57,100 If this player has a negative user ID, meaning it's a non real player account, then we can say unable 342 00:24:57,100 --> 00:25:02,290 to grab data for non real player accounts and then we'll just return like zero. 343 00:25:03,150 --> 00:25:07,890 So now if we go and play our game, everything should still be functioning fine. 344 00:25:07,890 --> 00:25:10,980 As you can see, we still have my money right here. 345 00:25:10,980 --> 00:25:12,210 All is well. 346 00:25:12,660 --> 00:25:19,680 And then if I, um, actually a cool demonstration that we could do is we can purposefully have this 347 00:25:19,680 --> 00:25:21,120 section of our code error. 348 00:25:21,120 --> 00:25:27,300 So what I'm going to do is I'm just going to type some random stuff in there or actually, no, I'll 349 00:25:27,300 --> 00:25:31,830 call the error function to force an error to occur right here. 350 00:25:33,060 --> 00:25:35,910 Which will result in this boolean being false. 351 00:25:35,910 --> 00:25:38,850 And that means we're going to be set inside of this table. 352 00:25:38,850 --> 00:25:42,720 And I want to test to see whether or not my data gets overwritten or not. 353 00:25:42,930 --> 00:25:45,090 So if I go and play test the game. 354 00:25:46,230 --> 00:25:50,670 As you can see, that error occurred and we got failed to grab money data for player. 355 00:25:50,670 --> 00:25:54,840 It passed our ID and then it gave us the error which was hahahahaha. 356 00:25:54,840 --> 00:25:58,710 And as you can see now my money value is at zero, which kind of sucks. 357 00:25:58,710 --> 00:26:03,870 And then if I go and leave the game, hopefully it's not going to save my data. 358 00:26:03,870 --> 00:26:08,580 And instead a warning should spit out saying we cannot save data for Arab players. 359 00:26:08,580 --> 00:26:09,840 So if I leave. 360 00:26:11,380 --> 00:26:12,250 There we go. 361 00:26:12,250 --> 00:26:17,170 Cannot save data for error players and we prevented that data loss from occurring. 362 00:26:17,170 --> 00:26:23,530 So if I go and remove this error and then I go and play my game again. 363 00:26:25,500 --> 00:26:30,000 As you can see, I still have my $100 stored in the data store. 364 00:26:31,210 --> 00:26:37,390 So that is how we can use the data store service to load and save data for players in our game. 365 00:26:37,390 --> 00:26:42,160 In the next lecture, we're going to be taking a look at how to grab the players with the most money 366 00:26:42,160 --> 00:26:45,100 on our game using ordered data stores. 367 00:26:45,100 --> 00:26:46,240 See you there!